package com.race.car;

import org.andengine.engine.handler.IUpdateHandler;
import org.andengine.entity.IEntity;
import org.andengine.entity.primitive.Rectangle;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.shape.RectangularShape;
import org.andengine.entity.sprite.AnimatedSprite;
import org.andengine.extension.physics.box2d.PhysicsConnector;
import org.andengine.extension.physics.box2d.PhysicsFactory;
import org.andengine.extension.physics.box2d.PhysicsWorld;
import org.andengine.opengl.texture.region.ITiledTextureRegion;
import org.andengine.opengl.vbo.VertexBufferObjectManager;
import org.andengine.util.color.Color;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.joints.RevoluteJoint;
import com.badlogic.gdx.physics.box2d.joints.RevoluteJointDef;
import com.badlogic.gdx.physics.box2d.joints.WeldJointDef;
import com.race.map.RaceMap;

public class Car implements IUpdateHandler, ICar {
	
	static public final float
		MAX_STEER_ANGLE = (float)Math.PI/3,
		HORSEPOWER = 4,
		STEER_SPEED = 1.5f;
	static private FixtureDef 
		DEFAULT_FIXTURE = PhysicsFactory.createFixtureDef(1, 0, 1),
		WHEEL_FIXTURE = PhysicsFactory.createFixtureDef(1, 0, 1, true);
	
	private Body carBody,leftWheel,rightWheel,rightRearWheel,leftRearWheel;
	private ITiledTextureRegion mTiledTextureRegion;
	private PhysicsWorld mPhysicsWorld;
	private VertexBufferObjectManager mVertexBufferObjectManager;
	private Scene mScene;
	private RectangularShape carShape;
	private float mBodyWidth,mBodyHeight,mWheelWidth,mWheelHeight;
	private float mX,mY;
	private float mSteeringAngle,mEngineSpeed;
	private RevoluteJoint mLeftJoint,mRightJoint;
	private RaceMap mMap;
	private Vector2 axis=new Vector2();
	public Car(float pX,float pY,ITiledTextureRegion pTiledTextureRegion,VertexBufferObjectManager pVertexBufferObjectManager,PhysicsWorld pPhysicsWorld,Scene pScene){
		mTiledTextureRegion=pTiledTextureRegion;
		mPhysicsWorld=pPhysicsWorld;
		mVertexBufferObjectManager=pVertexBufferObjectManager;
		mScene=pScene;
		mX=pX;
		mY=pY;
		carShape=new AnimatedSprite(mX, mY, mTiledTextureRegion, mVertexBufferObjectManager);

		init();
		
	}
	public Car(float pX,float pY,float pWidth,float pHeight,VertexBufferObjectManager pVertexBufferObjectManager,PhysicsWorld pPhysicsWorld,Scene pScene){
		mPhysicsWorld=pPhysicsWorld;
		mVertexBufferObjectManager=pVertexBufferObjectManager;
		mScene=pScene;
		mX=pX;
		mY=pY;
		carShape=new Rectangle(mX, mY, pWidth, pHeight, mVertexBufferObjectManager);
		carShape.setColor(Color.RED);
		init();
		
	}
	
	private void init(){
		mBodyHeight=carShape.getHeight();
		mBodyWidth=carShape.getWidth();
		mWheelHeight=mBodyHeight/6;
		mWheelWidth=Math.min(mBodyHeight/3, mBodyWidth/2);
		carBody=PhysicsFactory.createBoxBody(mPhysicsWorld, carShape, BodyType.DynamicBody,DEFAULT_FIXTURE);
		carBody.setLinearDamping(1f);
		carBody.setAngularDamping(1f);
		
		mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(carShape, carBody));
		
		Rectangle 
			leftRearWheelShape = new Rectangle(mX, mY-mWheelHeight/2, mWheelWidth, mWheelHeight, mVertexBufferObjectManager),
			righeRearWheelShape = new Rectangle(mX, mY+mBodyHeight-mWheelHeight/2, mWheelWidth, mWheelHeight, mVertexBufferObjectManager),
			leftWheelShape = new Rectangle(mX+mBodyWidth-mWheelWidth, mY-mWheelHeight/2, mWheelWidth, mWheelHeight, mVertexBufferObjectManager),
			rightWheelShape = new Rectangle(mX+mBodyWidth-mWheelWidth,mY+mBodyHeight-mWheelHeight/2, mWheelWidth, mWheelHeight, mVertexBufferObjectManager);

		leftRearWheel = PhysicsFactory.createBoxBody(mPhysicsWorld, leftRearWheelShape, BodyType.DynamicBody, WHEEL_FIXTURE);
		rightRearWheel = PhysicsFactory.createBoxBody(mPhysicsWorld, righeRearWheelShape, BodyType.DynamicBody, WHEEL_FIXTURE);
		leftWheel=PhysicsFactory.createBoxBody(mPhysicsWorld, leftWheelShape,BodyType.DynamicBody,WHEEL_FIXTURE);
		rightWheel=PhysicsFactory.createBoxBody(mPhysicsWorld, rightWheelShape,BodyType.DynamicBody,WHEEL_FIXTURE);
		
		mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(leftRearWheelShape,leftRearWheel));
		mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(leftWheelShape,leftWheel));
		mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(righeRearWheelShape,rightRearWheel));
		mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(rightWheelShape,rightWheel));
		mScene.attachChild(rightWheelShape);
		mScene.attachChild(leftWheelShape);
		mScene.attachChild(righeRearWheelShape);
		mScene.attachChild(leftRearWheelShape);
		
		RevoluteJointDef leftWheelDef=new RevoluteJointDef(),rightWheelDef=new RevoluteJointDef();
		leftWheelDef.initialize(carBody, leftWheel, leftWheel.getWorldCenter());
		leftWheelDef.enableMotor=true;
		leftWheelDef.maxMotorTorque=100;
		rightWheelDef.initialize(carBody, rightWheel, rightWheel.getWorldCenter());
		rightWheelDef.enableMotor=true;
		rightWheelDef.maxMotorTorque=100;
		mLeftJoint=(RevoluteJoint) mPhysicsWorld.createJoint(leftWheelDef);
		mRightJoint=(RevoluteJoint) mPhysicsWorld.createJoint(rightWheelDef);
		
		WeldJointDef leftRearDef=new WeldJointDef(),rightRearDef=new WeldJointDef();
		leftRearDef.initialize(carBody, leftRearWheel, leftRearWheel.getWorldCenter());
		rightRearDef.initialize(carBody, rightRearWheel, rightRearWheel.getWorldCenter());
		mPhysicsWorld.createJoint(leftRearDef);
		mPhysicsWorld.createJoint(rightRearDef);
		
		mScene.attachChild(carShape);
		mScene.registerUpdateHandler(this);
	}
	
	private void killOrthogonalVelocity(Body mBody){
		Vector2 vel=mBody.getLinearVelocity();
		float agl=mBody.getAngle();
		axis.x=(float)Math.cos(agl);axis.y=(float)Math.sin(agl);
		axis.mul(axis.dot(vel));
		mBody.setLinearVelocity(axis);
	}
	@Override
	public Vector2 getVelocity() {
		if(carBody!=null){
			return carBody.getLinearVelocity();
		}
		return null;
	}

	@Override
	public float getAngle() {
		if(carBody!=null){
			return carBody.getAngle();
		}
		return 0;
	}
	
	@Override
	public void setDamping(float pDamping) {
		carBody.setLinearDamping(pDamping);
	}

	@Override
	final public Body getBody() {
		return carBody;
	}

	@Override
	public void onUpdate(float pSecondsElapsed) {
		if(pSecondsElapsed<0.000001f)return;
		killOrthogonalVelocity(leftWheel);
		killOrthogonalVelocity(rightWheel);
		killOrthogonalVelocity(leftRearWheel);
		killOrthogonalVelocity(rightRearWheel);
		
		//Driving
		float agl;
		agl=leftWheel.getAngle();
		axis.x=(float)Math.cos(agl);axis.y=(float)Math.sin(agl);
		leftWheel.applyForce(axis.mul(mEngineSpeed),leftWheel.getPosition());
		agl=rightWheel.getAngle();
		axis.x=(float)Math.cos(agl);axis.y=(float)Math.sin(agl);
		rightWheel.applyForce(axis.mul(mEngineSpeed),rightWheel.getPosition());
		
		//Steering
		float mspeed;
		mspeed = mSteeringAngle - mLeftJoint.getJointAngle();
		mLeftJoint.setMotorSpeed(mspeed * STEER_SPEED);
		mspeed = mSteeringAngle - mRightJoint.getJointAngle();
		mRightJoint.setMotorSpeed(mspeed * STEER_SPEED);
		if (mMap!=null) {
			mMap.setCycles(this);
		}
	}

	@Override
	public void reset() {
		// TODO Auto-generated method stub

	}

	@Override
	public void onSteering(float pAngle) {
		mSteeringAngle=pAngle;
	}

	@Override
	public void onDriving(float pSpeed) {
		mEngineSpeed=pSpeed;
	}
	@Override
	public RectangularShape getShape(){
		return carShape;
	}
	public void registerMap(RaceMap pMap){
		mMap=pMap;
	}
	private int mGoldSum;
	synchronized public void setGoldNum(int m){
		mGoldSum=m;
	}
	public int getGoldNum(){
		return mGoldSum;
	}
	private int mAcceleratorSum;
	synchronized public void setAcceleratorNum(int m){
		mAcceleratorSum=m;
	}
	public int getAcceleratorNum(){
		return mAcceleratorSum;
	}
	private int mCycles;
	public int getCycles(){
		return mCycles;
	}
	public void setCycles(int cycles){
		mCycles=cycles;
	}
	public void setPosition(float x,float y)
	{
		carShape.setPosition(x, y);
	}
	public float[] getHeadCoordinates(){
		float pos[]=getShape().convertLocalToSceneCoordinates(getShape().getWidth(),getShape().getHeight()/2);
		return pos;
	}
}
